package xuecheng.spring.boot.start.common.http;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * http 工具类
 *
 * @author PengXueCheng
 * @version 1.0.0
 * @mail yingcheng1016@gmail.com
 * @date 2019/4/25 10:58
 * @desc
 */
public interface HttpUtils {

    //############################### 常量字段定义
    /**
     * "application/x-www-form-urlencoded"，是默认的MIME内容编码类型，一般可以用于所有的情况，但是在传输比较大的二进制或者文本数据时效率低。
     * 这时候应该使用"multipart/form-data"。如上传文件或者二进制数据和非ASCII数据。
     */
    String MEDIA_TYPE_NORAML_FORM = "application/x-www-form-urlencoded;charset=utf-8";
    /**
     * 既可以提交普通键值对，也可以提交(多个)文件键值对。
     */
    String MEDIA_TYPE_MULTIPART_FORM = "multipart/form-data;charset=utf-8";
    /**
     * 只能提交二进制，而且只能提交一个二进制，如果提交文件的话，只能提交一个文件,后台接收参数只能有一个，而且只能是流（或者字节数组）
     */
    String MEDIA_TYPE_STREAM = "application/octet-stream";
    /**
     * 文本格式
     */
    String MEDIA_TYPE_TEXT = "text/plain;charset=utf-8";
    /**
     * json 格式
     */
    String MEDIA_TYPE_JSON = "application/json;charset=utf-8";

    /**
     * 耗时项格式化输出
     */
    String W_STR = "\\n" +
            "http_code:\\t%{http_code}\\n" +
            "-------------------------\\n" +
            "耗时项\\t耗时(单位:秒)\\n" +
            "time_namelookup:\\t%{time_namelookup}\\n" +
            "time_connect:\\t%{time_connect}\\n" +
            "time_appconnect:\\t%{time_appconnect}\\n" +
            "time_redirect:\\t%{time_redirect}\\n" +
            "time_pretransfer:\\t%{time_pretransfer}\\n" +
            "time_starttransfer:\\t%{time_starttransfer}\\n" +
            "time_total:\\t%{time_total}\\n";
    /**
     * 单个请求头格式化
     */
    String HEADER_FORMAT = " -H '%s' ";
    /**
     * curl 格式 请求头, 请求类型(GET, POST), 耗时项输出, 请求体, 请求 URL
     * <p>
     * String GET_CURL_FORMAT = "curl %s -X GET -w '%s' -d '%s' '%s'";
     * String POST_CURL_FORMAT = "curl %s -X POST -w '%s' -d '%s' '%s'";
     */
    String CURL_FORMAT = "curl %s -X %s -w '%s' -d '%s' '%s'";
    /**
     * post 请求类型
     */
    String METHOD_POST = "POST";
    /**
     * get 请求类型
     */
    String METHOD_GET = "GET";


    //############################### 静态方法定义

    /**
     * 对请求参数进行按 key 升序排序后， 按 key1=value1&key2=value2 的格式拼接成字符串(键值对均进行URL_ENCODE)
     *
     * @param params 参数键值对
     * @return 连接在 URL 后面的参数字符串(不含?)
     */
    static String map2QueryStringWithEncode(Map<String, Object> params) {
        Collection<String> keySet = params.keySet();
        List<String> list = new ArrayList<>(keySet);
        Collections.sort(list);
        StringBuffer sb = new StringBuffer();
        String sp1 = "=";
        String sp2 = "&";
        for (int i = 0; i < list.size(); i++) {
            String key = list.get(i);
            try {
                key = URLEncoder.encode(list.get(i), StandardCharsets.UTF_8.name());
            } catch (UnsupportedEncodingException ignored) {
            }
            String value = params.get(list.get(i)).toString();
            try {
                value = URLEncoder.encode(value, StandardCharsets.UTF_8.name());
            } catch (UnsupportedEncodingException ignored) {
            }
            sb.append(key).append(sp1).append(value);
            if (i < list.size() - 1) {
                sb.append(sp2);
            }
        }
        return sb.toString();
    }

    /**
     * 对请求参数进行按 key 升序排序后， 按 key1=value1&key2=value2 的格式拼接成字符串(键值对不均进行URL_ENCODE)
     *
     * @param params 参数键值对
     * @return 连接在 URL 后面的参数字符串(不含?)
     */
    static String map2QueryString(Map<String, Object> params) {
        Collection<String> keySet = params.keySet();
        List<String> list = new ArrayList<>(keySet);
        Collections.sort(list);
        StringBuffer sb = new StringBuffer();
        String sp1 = "=";
        String sp2 = "&";
        for (int i = 0; i < list.size(); i++) {
            String key = list.get(i);
            String value = params.get(list.get(i)).toString();
            sb.append(key).append(sp1).append(value);
            if (i < list.size() - 1) {
                sb.append(sp2);
            }
        }
        return sb.toString();
    }


    //############################### 接口定义

    /**
     * 获取请求对应的 curl
     *
     * @param url     请求URL
     * @param content 请求体
     * @param headers 请求头
     * @param method  请求类型
     * @return curl 字符串
     */
    default String getCurl(String url, String content, Map<String, String> headers, String method) {
        headers = Optional.ofNullable(headers).orElse(new TreeMap<>());
        String headersStr = headers.entrySet().stream().map(entry -> {
            String key = entry.getKey();
            String value = entry.getValue();
            String header = String.format("%s:%s", key, value);
            return String.format(HEADER_FORMAT, header);
        }).collect(Collectors.joining(""));
        return String.format(CURL_FORMAT, headersStr, method, W_STR, content, url);
    }

    /**
     * 发送 GET 请求
     *
     * @param url     请求 URL
     * @param headers 请求头(HTTP报头的名称是不区分大小写)
     * @return http 响应结果
     */
    SimpleEvent get(String url, Map<String, String> headers);

    /**
     * 发送 GET 请求
     *
     * @param url 请求 URL
     * @return http 响应结果
     */
    SimpleEvent get(String url);

    /**
     * 发送 POST(content-type: application/json;)
     *
     * @param url           请求 URL
     * @param contentObject 请求 URL 参数对象
     * @return http 响应结果
     */
    SimpleEvent post(String url, Object contentObject);

    /**
     * 发送 POST(content-type: application/json;)
     *
     * @param url     请求 URL
     * @param content 请求 URL 参数
     * @return http 响应结果
     */
    SimpleEvent post(String url, String content);

    /**
     * 发送 POST 请求, 并且添加 指定 header
     *
     * @param url     请求 URL
     * @param content 请求 URL 参数
     * @param headers 请求头(HTTP报头的名称是不区分大小写)
     * @return http 响应结果
     */
    SimpleEvent post(String url, String content, Map<String, String> headers);

    /**
     * 发送 POST 请求, 并且添加 指定 header
     *
     * @param url     请求 URL
     * @param content 请求 URL 参数字节数组
     * @param headers 请求头(HTTP报头的名称是不区分大小写)
     * @return http 响应结果
     */
    SimpleEvent post(String url, byte[] content, Map<String, String> headers);

    /**
     * 发送指定 mediaType POST 请求, 并且添加 指定 header
     *
     * @param url       请求 URL
     * @param content   请求 URL 参数
     * @param mediaType 请求类型
     * @param headers   请求头(HTTP报头的名称是不区分大小写)
     * @return http 响应结果
     */
    SimpleEvent post(String url, String content, String mediaType, Map<String, String> headers);

    /**
     * 发送指定 mediaType POST 请求, 并且添加 指定 header
     *
     * @param url       请求 URL
     * @param content   请求 URL 参数字节数组
     * @param mediaType 请求类型
     * @param headers   请求头(HTTP报头的名称是不区分大小写)
     * @return http 响应结果
     */
    SimpleEvent post(String url, byte[] content, String mediaType, Map<String, String> headers);

//	/**
//	 * 发送 GET 请求
//	 *
//	 * @param url    请求 URL
//	 * @param aClass json 解析字节码类型和订阅事件
//	 */
//	void get(String url, Class<? extends SimpleEvent> aClass);
//
//	/**
//	 * 发送 POST(content-type: application/json;)
//	 *
//	 * @param url           请求 URL
//	 * @param contentObject 请求体参数对象
//	 * @param aClass        json 解析字节码类型和订阅事件
//	 */
//	void post(String url, Object contentObject, Class<? extends SimpleEvent> aClass);
//
//	/**
//	 * 发送 POST(content-type: application/json;)
//	 *
//	 * @param url     请求 URL
//	 * @param content 请求体参数
//	 * @param aClass  json 解析字节码类型和订阅事件
//	 */
//	void post(String url, String content, Class<? extends SimpleEvent> aClass);

}
